package com.koron.common.web.controller;

import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadCallback;
import com.github.tobato.fastdfs.service.DefaultFastFileStorageClient;
import com.koron.bean.base.Attachment;
import com.koron.bean.base.Response;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.common.core.business.common.service.AttachmentService;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.web.controller.attachment.FileCopyParam;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author qingfeng
 * @description: 附件上传
 * @date 2021/4/26 11:21
 */
@RestController
@RequestMapping(value = "/attachment")
@Api(tags = "系统附件")
@Slf4j
public class AttachmentController {

    @Autowired
    private AttachmentService service;

    @Autowired
    private DefaultFastFileStorageClient storageClient;

    private EamUser getCurrUser() {
        return ThreadLocalContext.get();
    }

    private void setCurrDs(String currDs) {
        if (!StringUtils.isEmpty(currDs)) {
            getCurrUser().setCurrDs(currDs);
        }
    }

    @ApiOperation("附件上传（删除原先的业务附件, 每次上传, 需上传所有的附件）")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessId", value = "业务ID", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "subBusinessId", value = "子业务ID", required = false, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query", dataType = "String")})
    @PostMapping("/upload")
    public Response<List<Attachment>> uploadAttach(
            @RequestParam String businessId,
            @RequestParam(required = false) String subBusinessId,
            @RequestParam(required = false) String businessType,
            @RequestParam MultipartFile[] files) {
        try {
            return Response.success(service.uploadAttachmentWithTypeWithSubBusinessId(businessId, subBusinessId, businessType, files));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Response.fail(e.getMessage());
        }
    }

    @ApiOperation("附件上传（单个）,不会删除原来附件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessId", value = "业务ID", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "subBusinessId", value = "子业务ID", required = false, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query", dataType = "String")})
    @PostMapping("/uploadSingle")
    public Response<Attachment> uploadSingleAttachment(
            @RequestParam String businessId,
            @RequestParam(required = false) String subBusinessId,
            @RequestParam(required = false) String businessType,
            @RequestParam MultipartFile file) {
        try {
            return Response.success(service.uploadSingleAttachmentWithTypeWithSubBussinessId(businessId, subBusinessId, businessType, file));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Response.fail(e.getMessage());
        }
    }

    @ApiOperation("追加附件list,不会删除全部原来附件,根据deleteFileIds有需要的删除")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessId", value = "业务ID", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "subBusinessId", value = "子业务ID", required = false, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "deleteFileIds", value = "待删除文件id数组", required = false, paramType = "query", allowMultiple = true, dataType = "String")
    })
    @PostMapping("/appendAttachmentList")
    public Response<List<Attachment>> appendAttachmentList(
            @RequestParam String businessId,
            @RequestParam(required = false) String subBusinessId,
            @RequestParam(required = false) String businessType,
            @RequestParam(required = false) String[] deleteFileIds,
            @RequestParam MultipartFile[] files) {
        try {
            return Response.success(service.appendAttachmentWithTypeWithSubBusinessId(businessId, subBusinessId, businessType, deleteFileIds, files));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Response.fail(e.getMessage());
        }
    }

    /**
     * 附件预览或下载
     * fileId与attachmentUrl二选一，若都传入，优先使用fileId
     * eg1: ip:port/attachment/preview?fileId=***            ===>   预览
     * eg2: ip:port/attachment/preview/{attachmentUrl}       ===>   预览
     * eg3: ip:port/attachment/download?fileId=***           ===>   下载
     * eg4: ip:port/attachment/download/{attachmentUrl}      ===>   下载
     *
     * @param fileId   附件id
     * @param response 请求相应
     * @param request  请求
     */
    @ApiOperation("附件预览或下载, fileId为附件id,currDs为当前数据源标识,作为参数。attachmentUrl为附件url, 拼接在请求路径后。fileId与attachmentUrl二选一，若都传入，优先使用fileId")
    @GetMapping({"/download", "/preview", "/download/**", "/preview/**"})
    public void download(String currDs, String fileId, HttpServletResponse response, HttpServletRequest request) {
        setCurrDs(currDs);
        String contextPath = request.getServletPath();
        contextPath = StringUtils.replace(contextPath, "/attachment", "");
        // 是否是预览文件
        boolean isPreview = false;
        if (StringUtils.startsWith(contextPath, "/preview")) {
            isPreview = true;
        }
        Attachment attachment = null;
        if (StringUtils.isNotBlank(fileId)) {
            attachment = service.getById(fileId);
        } else {
            int startIdx = StringUtils.indexOf(contextPath, isPreview ? "preview" : "download") + (isPreview ? 8 : 9);
            int endIdx = StringUtils.indexOf(contextPath, "?") > -1 ? StringUtils.indexOf(contextPath, "?") : contextPath.length();
            // 前端传入的文件路径
            String url = StringUtils.substring(contextPath, startIdx, endIdx);
            attachment = service.getByUrl(url);
        }
        try (OutputStream os = response.getOutputStream()) {
            if (attachment == null) {
                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                os.write("附件不存在".getBytes());
                return;
            }
            if (!isPreview) {
                response.setHeader("Content-Type", "application/octet-stream;charset=UTF-8");
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(attachment.getName(), "UTF-8"));
            }
            StorePath storePath = StorePath.parseFromUrl(attachment.getPath());
            storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), (DownloadCallback<ResponseEntity>) inputStream -> {
                byte[] buffer = new byte[10240];
                BufferedInputStream bis = new BufferedInputStream(inputStream);
                int i = bis.read(buffer);
                while (i != -1) {
                    os.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
                inputStream.close();
                return null;
            });
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            try (OutputStream os = response.getOutputStream()) {
                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                os.write("附件下载失败".getBytes());
            } catch (Exception e1) {
                log.error(e.getMessage(), e1);
            }
        }
    }

    @GetMapping("/attachmentList")
    @ApiOperation("获取业务附件列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessId", value = "业务id", required = true, paramType = "query"),
            @ApiImplicitParam(name = "subBusinessId", value = "子业务ID", required = false, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query")

    })
    public Response attachmentList(
            @RequestParam String businessId,
            @RequestParam(required = false) String subBusinessId,
            @RequestParam(required = false) String businessType) {
        List<Attachment> list = service.getAttachmentListByBusinessIdWithTypeWithSubBusinessId(businessId, subBusinessId, businessType);
        return Response.success("查询成功", list);
    }

    @GetMapping("/attachmentListByBusinessIds")
    @ApiOperation("获取业务ids附件列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessIds", value = "业务ids", required = true, paramType = "query", allowMultiple = true, dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query")

    })
    public Response getAttachmentListByBusinessIdsWithType(
            @RequestParam String[] businessIds,
            @RequestParam(required = false) String businessType) {
        List<Attachment> list = service.getAttachmentListByBusinessIdsWithType(businessIds, businessType);
        return Response.success("查询成功", list);
    }

    @GetMapping("/delete")
    @ApiOperation("删除单个附件通过文件id")
    @ApiImplicitParam(name = "fileId", value = "附件id", required = true, paramType = "query")
    public Response<Integer> delete(@RequestParam String fileId) {
        return Response.success("删除成功", service.deleteAttachment(new String[]{fileId}));
    }

    @GetMapping("/deleteByFileIds")
    @ApiOperation("删除多个附件通过文件ids")
    @ApiImplicitParam(name = "fileIds", value = "附件ids", required = true, paramType = "query", allowMultiple = true, dataType = "String")
    public Response<Integer> deleteByFileIds(@RequestParam String[] fileIds) {
        return Response.success("删除成功", service.deleteAttachment(fileIds));
    }

    @GetMapping("/deleteByBusinessIdWithType")
    @ApiOperation("删除附件通过业务id,子业务id,业务类型")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessId", value = "业务id", required = true, paramType = "query"),
            @ApiImplicitParam(name = "subBusinessId", value = "子业务id", required = false, paramType = "query"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query")
    })
    public Response<Integer> deleteByBusinessIdWithType(
            @RequestParam String businessId,
            @RequestParam(required = false) String subBusinessId,
            @RequestParam(required = false) String businessType) {
        return Response.success("删除成功", service.deleteAttachmentWithTypeWithSubBusinessId(businessId, subBusinessId, businessType));
    }

    @GetMapping("/deleteByBusinessIdsWithType")
    @ApiOperation("删除附件通过业务ids,业务类型")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "businessIds", value = "业务ids", required = true, paramType = "query", allowMultiple = true, dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query")
    })
    public Response<Integer> deleteByBusinessIdsWithType(
            @RequestParam String[] businessIds,
            @RequestParam(required = false) String businessType) {
        return Response.success("删除成功", service.deleteByBusinessIdsWithType(businessIds, businessType));
    }




    /**
     *  @author xxxn
     *  @description: 附件批量下载成压缩包
     *  @date 2022/07/21
     *  @param fileIds   附件id
     *  @param response 请求相应
     *  @param request  请求
     */
    @ApiOperation("附件预览或下载, currDs为当前数据源标识,作为参数。fileIds,businessId二者必须传一个")
    @GetMapping({"/downloadZip"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "zipName", value = "附件的名称(默认：附件)", required = false, paramType = "query"),
            @ApiImplicitParam(name = "fileIds", value = "压缩包需要包含的附件的id(与业务id必须传其中一个),多个用逗号分隔开来", required = false, paramType = "query"),
            @ApiImplicitParam(name = "businessId", value = "业务id(与附件id必须传其中一个)", required = false, paramType = "query"),
            @ApiImplicitParam(name = "subBusinessId", value = "子业务ID", required = false, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "businessType", value = "业务类型", required = false, paramType = "query")

    })
    public void downloadByZip(String currDs,@RequestParam(defaultValue = "附件",required = false) String zipName,
                              @RequestParam(required = false) String fileIds,
                              @RequestParam(required = false) String businessId,
                              @RequestParam(required = false) String subBusinessId,
                              @RequestParam(required = false) String businessType,
                              HttpServletResponse response, HttpServletRequest request) {
        setCurrDs(currDs);
        if (StringUtils.isAllEmpty(fileIds, businessId)) {
            try (OutputStream os = response.getOutputStream()) {
                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                os.write("附件fileIds或businessId不能都为空".getBytes());
                return;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        List<Attachment> attachmentList = new ArrayList<>();
        if (StringUtils.isNotBlank(fileIds)) {
            attachmentList.addAll(service.getByIds(fileIds));
        }
        if (StringUtils.isNotBlank(businessId)) {
            attachmentList.addAll(service.getAttachmentListByBusinessIdWithTypeWithSubBusinessId(businessId,subBusinessId,businessType));
        }
        try  {
            if (CollectionUtils.isEmpty(attachmentList)) {
                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                response.getOutputStream().write("附件不存在".getBytes());
                return;
            }
            byte[] buffers = new byte[1024];
            // 创建一个新的 byte 数组输出流，它具有指定大小的缓冲区容量
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //创建一个新的缓冲输出流，以将数据写入指定的底层输出流
            BufferedOutputStream fos = new BufferedOutputStream(baos);
            ZipOutputStream zos = new ZipOutputStream(fos);

            //如果文件重名了,则加上编号(目前这里编号存在点问题，后续有空继续修改,暂时不报错就行)
            HashSet<String> fileNamesSet = new HashSet<>();
            int fileNum = 1;

            for (int j = 0; j < attachmentList.size(); j++) {
                //获取各个文件的数据流
                Attachment attachmentOne = attachmentList.get(j);

                if(fileNamesSet.contains(attachmentOne.getName())){
                    String s1 = StringUtils.substringBeforeLast(attachmentOne.getName(), ".");
                    String s2 = StringUtils.substringAfterLast(attachmentOne.getName(), ".");
                    attachmentOne.setName(s1+"("+fileNum+")"+"."+s2);
                    fileNum++;
                }
                fileNamesSet.add(attachmentOne.getName());

                StorePath storePath = StorePath.parseFromUrl(attachmentOne.getPath());
                storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), (DownloadCallback<ResponseEntity>) inputStream -> {
                    zos.putNextEntry(new ZipEntry(attachmentOne.getName()));
                    int length;
                    while ((length = inputStream.read(buffers)) > 0) {
                        //将文件读入压缩文件内
                        zos.write(buffers, 0, length);
                    }
                    zos.closeEntry();
                    inputStream.close();
                    return null;
                });
            }
            zos.close();
            fos.flush();

            OutputStream os = response.getOutputStream();
            InputStream is = new ByteArrayInputStream(baos.toByteArray());

            byte[] buffer1 = new byte[1024 * 5];
            int len = 0;
            while ((len = is.read(buffer1)) > 0) {
                os.write(buffer1, 0, len);
            }
            os.flush();
            baos.close();
            fos.close();
            response.setHeader("Content-Type", "application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipName, "UTF-8"));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            try (OutputStream os = response.getOutputStream()) {
                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                os.write("附件下载失败".getBytes());
            } catch (Exception e1) {
                log.error(e.getMessage(), e1);
            }
        }
    }

    @PutMapping("/copySourcesToTargets")
    @ApiOperation("批量复制文件根据业务id")
    public Response<String> copySourceToTargetBatch(@RequestBody List<FileCopyParam> fileCopyParamList) {
        for (FileCopyParam fileCopyParam : fileCopyParamList) {
            service.copyAttachmentWithTypeWithSubBusinessIdWithFileIds(fileCopyParam.getTargetId(),
                    fileCopyParam.getSourceFileIds(), fileCopyParam.getTargetSubId(), fileCopyParam.getTargetType(),
                    fileCopyParam.getSourceId(), fileCopyParam.getSourceSubId(), fileCopyParam.getSourceType());
        }
        return Response.success("复制成功");
    }

}
